package com.xhg.mqtt;

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

/**
 * 密码工具类 MD5+salt 算法
 */
public class PasswordUtils {
    private static final String[] ENCRYPT_CHAR_USER;
    private static final String[] ENCRYPT_CHAR_ADMIN;
    private static final char[] DIGITS_HEX;

    public enum EncryptChar {
        TYPE_U, TYPE_A
    }

    static {
        DIGITS_HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        ENCRYPT_CHAR_USER = new String[]{"0123456789", "ABCDEFHIGK", "lmnopkrstu", "!@#$%&.*^/", "LMNOPKRSTU", "9876543210", "abcdefhigk", "(+-~?),:;|"};
        ENCRYPT_CHAR_ADMIN = new String[]{"ABCDEFHIGK", "9876543210", "abcdefhigk", "(+-~?),:;|", "lmnopkrstu", "0123456789", "LMNOPKRSTU", "!@#$%&.*^/"};
    }

    /**
     * 加密明文密码
     *
     * @param password    明文密码
     * @param encryptChar 加密字符
     * @return 加密后的密码
     */
    public static String encrypt(String password, EncryptChar encryptChar) {
        Random random = new Random();
        StringBuilder noiseBuilder = new StringBuilder();
        StringBuilder numBuilder = new StringBuilder();
        int[][] randomNum = new int[8][2];
        switch (encryptChar) {
            case TYPE_U:
                for (int idx = 0; idx < randomNum.length; idx++) {
                    noiseBuilder.append(ENCRYPT_CHAR_USER[randomNum[idx][0] = random.nextInt(8)].charAt(randomNum[idx][1] = random.nextInt(10)));
                    numBuilder.append(randomNum[idx][0]).append(randomNum[idx][1]);
                }
                break;
            case TYPE_A:
                for (int idx = 0; idx < randomNum.length; idx++) {
                    noiseBuilder.append(ENCRYPT_CHAR_ADMIN[randomNum[idx][0] = random.nextInt(8)].charAt(randomNum[idx][1] = random.nextInt(10)));
                    numBuilder.append(randomNum[idx][0]).append(randomNum[idx][1]);
                }
                break;
        }
        String salt = md5Hex(noiseBuilder.append(numBuilder).toString());
        password = encoder(password,salt);
//        char[] cs = new char[48];
//        for (int idx = 0; idx < 48; idx += 3) {
//            cs[idx] = password.charAt(idx / 3 * 2);
//            char c = numBuilder.charAt(idx / 3);
//            cs[idx + 1] = c;
//            cs[idx + 2] = password.charAt(idx / 3 * 2 + 1);
//        }
        System.out.println("salt-->"+salt);
        return password;
    }

    /**
     * 验证密码是否正确
     *
     * @param password    明文密码
     * @param encrypted   加密后的密码
     * @param encryptChar 加密字符
     * @return 正确-true 错误-false
     */
    public static boolean verify(String password, String encrypted, EncryptChar encryptChar) {
        char[] md5 = new char[32];
        int[] randomNum = new int[16];
        StringBuilder numBuilder = new StringBuilder();
        for (int idx = 0; idx < 48; idx += 3) {
            md5[idx / 3 * 2] = encrypted.charAt(idx);
            md5[idx / 3 * 2 + 1] = encrypted.charAt(idx + 2);
            randomNum[idx / 3] = Character.getNumericValue(encrypted.charAt(idx + 1));
            numBuilder.append(randomNum[idx / 3]);
        }
        StringBuilder noiseBuilder = new StringBuilder();
        switch (encryptChar) {
            case TYPE_U:
                for (int idx = 0; idx < randomNum.length; idx += 2) {
                    noiseBuilder.append(ENCRYPT_CHAR_USER[randomNum[idx]].charAt(randomNum[idx + 1]));
                }
                break;
            case TYPE_A:
                for (int idx = 0; idx < randomNum.length; idx += 2) {
                    noiseBuilder.append(ENCRYPT_CHAR_ADMIN[randomNum[idx]].charAt(randomNum[idx + 1]));
                }
                break;
        }
        String salt = md5Hex(noiseBuilder.append(numBuilder).toString());
        System.out.println("sss:"+salt);
        return md5Hex(password + salt).equals(String.valueOf(md5));
    }

    /**
     * 验证密码是否正确
     *
     * @param password    明文密码
     * @param encrypted   加密后的密码
     * @return 正确-true 错误-false
     */
    public static boolean verify(String password, String encrypted, String salt) {
        return encoder(password,salt).equals(String.valueOf(encrypted));
    }

    private static String md5Hex(String data) {
        try {
            MessageDigest md5Digest = MessageDigest.getInstance("MD5");
            byte[] md5Bytes = md5Digest.digest(data.getBytes(Charset.forName("UTF-8")));
            return String.valueOf(encodeHex(md5Bytes));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private static char[] encodeHex(byte[] data) {
        int len = data.length;
        char[] out = new char[len << 1];
        int idx = 0;
        for (int jdx = 0; idx < len; ++idx) {
            out[jdx++] = DIGITS_HEX[(240 & data[idx]) >>> 4];
            out[jdx++] = DIGITS_HEX[15 & data[idx]];
        }
        return out;
    }
    /**
     * @Des 得到相应的一个MD5加密后的字符串
     * @param psd
     * @param salt
     * @return    MD5加密后的字符串
     */
    public static String encoder(String psd, String salt) {
        try {
            StringBuffer stingBuffer = new StringBuffer();
            // 1.指定加密算法
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 2.将需要加密的字符串转化成byte类型的数据，然后进行哈希过程
            byte[] bs = digest.digest((psd + salt).getBytes());
            // 3.遍历bs,让其生成32位字符串，固定写法

            // 4.拼接字符串
            for (byte b : bs) {
                int i = b & 0xff;
                String hexString = Integer.toHexString(i);
                if (hexString.length() < 2) {
                    hexString = "0" + hexString;
                }
                stingBuffer.append(hexString);
            }
            return stingBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}